package com.itboyst.facedemo.controller;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.itboyst.facedemo.dto.FaceDetectResDTO;
import com.itboyst.facedemo.dto.ProcessInfo;
import com.itboyst.facedemo.dto.UserCompareInfo;
import com.itboyst.facedemo.entity.UserFaceInfo;
import com.itboyst.facedemo.enums.ErrorCodeEnum;
import com.itboyst.facedemo.rpc.BusinessException;
import com.itboyst.facedemo.rpc.Response;
import com.itboyst.facedemo.service.FaceEngineService;
import com.itboyst.facedemo.service.UserFaceInfoService;
import com.itboyst.facedemo.util.Base64Util;
import com.itboyst.facedemo.util.UserRamCache;
import net.nettime.mobileimsdk.server.netty.MBObserver;
import net.openmob.mobileimsdk.server.utils.LocalSendHelper;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.itboyst.facedemo.util.Base64Util.base64Process;


@Controller
public class FaceController {
    public final static Logger logger = LoggerFactory.getLogger(FaceController.class);
    @Autowired
    private FaceEngineService faceEngineService;
    @Autowired
    UserFaceInfoService userFaceInfoService;

    @Value("${server.port}")
    private int port;


    //初始化注册人脸，注册到本地内存
    @PostConstruct
    public void initFace() throws FileNotFoundException {
        Map<String, String> fileMap = Maps.newHashMap();
        fileMap.put("zhao1", "赵丽颖");
        fileMap.put("yang1", "杨紫");
        int index = 0;
        for (String f : fileMap.keySet()) {
            File file = ResourceUtils.getFile("classpath:static/images/" + f + ".jpg");
            ImageInfo rgbData = ImageFactory.getRGBData(file);
            List<FaceInfo> faceInfoList = faceEngineService.detectFaces(rgbData);
            if (CollectionUtil.isNotEmpty(faceInfoList)) {
                byte[] feature = faceEngineService.extractFaceFeature(rgbData, faceInfoList.get(0));
                UserFaceInfo userInfo = new UserFaceInfo();
                userInfo.setFaceId(index);
                userInfo.setName(fileMap.get(f));
                userInfo.setFaceFeature(feature);
                UserRamCache.addUser(userInfo);
            }
            index++;
        }

        logger.info("http://127.0.0.1:" + port + "/");

    }


    /*
    人脸添加
     */
    @RequestMapping(value = "/faceAdd", method = RequestMethod.POST)
    @ResponseBody
    public Response faceAdd(@RequestParam("file") String file, @RequestParam("groupId") Integer groupId, @RequestParam("name") String name) {
        ByteArrayInputStream in = null;
        try {
            if (file == null) {
                throw new BusinessException(ErrorCodeEnum.FAIL, "file is null");
            }
            if (groupId == null) {
                throw new BusinessException(ErrorCodeEnum.FAIL, "groupId is null");
            }
            if (name == null) {
                throw new BusinessException(ErrorCodeEnum.FAIL, "name is null");
            }

            byte[] decode = Base64.decode(base64Process(file));
            //将decode作为输入流；
            in = new ByteArrayInputStream(decode);
            //将in作为输入流，读取图片存入image中，而这里in可以为ByteArrayInputStream();
            BufferedImage bufferedImage = ImageIO.read(in);

            List<FaceDetectResDTO> faceDetectResDTOS = faceEngineService.detectFacesByAdd(bufferedImage);
            if (CollectionUtil.isNotEmpty(faceDetectResDTOS) && faceDetectResDTOS.size() > 0) {
                List<FaceInfo> faceInfoList = new ArrayList<>();
                for (FaceDetectResDTO faceDetectResDTO : faceDetectResDTOS) {
                    FaceInfo faceInfo = new FaceInfo();
                    faceInfo.setRect(faceDetectResDTO.getRect());
                    faceInfo.setFaceId(faceDetectResDTO.getFaceId());
                    faceInfo.setOrient(faceDetectResDTO.getOrient());
                    faceInfo.setWearGlasses(faceDetectResDTO.getWearGlasses());
                    faceInfo.setLeftEyeClosed(faceDetectResDTO.getLeftEyeClosed());
                    faceInfo.setRightEyeClosed(faceDetectResDTO.getRightEyeClosed());
                    faceInfo.setFaceShelter(faceDetectResDTO.getFaceShelter());
                    faceInfo.setFaceData(faceDetectResDTO.getFaceData());
                    faceInfoList.add(faceInfo);
                }
                //人脸特征获取
                ImageInfo imageInfo = ImageFactory.getRGBData(decode);
                byte[] bytes = faceEngineService.extractFaceFeature(imageInfo, faceInfoList.get(0));
                if (bytes == null) {
                    throw new BusinessException(ErrorCodeEnum.NO_FACE_DETECTED, "未发现人脸特征");
                }

                UserFaceInfo userFaceInfo = new UserFaceInfo();
                userFaceInfo.setName(name);
                userFaceInfo.setGroupId(groupId);
                userFaceInfo.setFaceFeature(bytes);
                userFaceInfo.setBaseImgPath(faceDetectResDTOS.get(0).getBaseImgPath());
                userFaceInfo.setFaceId(RandomUtil.randomInt(1000000));

                //人脸特征插入到数据库
                userFaceInfoService.insertSelective(userFaceInfo);
                UserRamCache.addUser(userFaceInfo);
            }

            logger.info("faceAdd:" + name);
            return Response.newSuccessResponse("");
        } catch (Exception e) {
            logger.error("", e);
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
        return Response.newFailedResponse(ErrorCodeEnum.FAIL);
    }

    /*
    人脸识别
     */
    @RequestMapping(value = "/faceRecognition", method = RequestMethod.POST)
    @ResponseBody
    public Response<List<FaceDetectResDTO>> faceRecognition(String image) {
        List<FaceDetectResDTO> faceDetectResDTOS = Lists.newLinkedList();
        byte[] bytes = Base64Util.base64ToBytes(image);
        ImageInfo rgbData = ImageFactory.getRGBData(bytes);
        long start = System.currentTimeMillis();
        List<FaceInfo> faceInfoList = faceEngineService.detectFaces(rgbData);
        if (CollectionUtil.isNotEmpty(faceInfoList)) {
            List<ProcessInfo> process = faceEngineService.process(rgbData, faceInfoList);

            for (int i = 0; i < faceInfoList.size(); i++) {
                FaceDetectResDTO faceDetectResDTO = new FaceDetectResDTO();
                FaceInfo faceInfo = faceInfoList.get(i);
                faceDetectResDTO.setRect(faceInfo.getRect());
                faceDetectResDTO.setOrient(faceInfo.getOrient());
                faceDetectResDTO.setFaceId(faceInfo.getFaceId());
                if (CollectionUtil.isNotEmpty(process)) {
                    ProcessInfo processInfo = process.get(i);
                    faceDetectResDTO.setAge(processInfo.getAge());
                    faceDetectResDTO.setGender(processInfo.getGender());
                    faceDetectResDTO.setLiveness(processInfo.getLiveness());
                }
                byte[] feature = faceEngineService.extractFaceFeature(rgbData, faceInfo);
                if (feature != null) {
                    List<UserCompareInfo> userCompareInfos = faceEngineService.faceRecognition(feature, UserRamCache.getUserList(), 0.8f);
                    if (CollectionUtil.isNotEmpty(userCompareInfos)) {
                        faceDetectResDTO.setFaceId(userCompareInfos.get(0).getFaceId());
                        String baseImgPath = userCompareInfos.get(0).getBaseImgPath();
                        if (Strings.isNotBlank(baseImgPath)) {
                            faceDetectResDTO.setBaseImgPath("base/" + baseImgPath);
                        }
                        faceDetectResDTO.setName(userCompareInfos.get(0).getName());
                        faceDetectResDTO.setSimilar(userCompareInfos.get(0).getSimilar());
                        //给客户端发送结果
                        try {
                            //String msg = "姓名:" + userCompareInfos.get(0).getName() + ",年龄:" + faceDetectResDTO.getAge() + ",性别:" + faceDetectResDTO.getGender();
                            String msg = "VIP铂金客户:" + userCompareInfos.get(0).getName() + (faceDetectResDTO.getGender() == 0 ? "先生" : "女士") + "来厂" + ",客户专员:王迪,上次进厂时间:2020年2月10日,年龄," + faceDetectResDTO.getAge();
                            LocalSendHelper.sendData(String.valueOf(1), msg, new MBObserver() {
                                @Override
                                public void update(boolean isSuccess, Object extraObj) {
                                    if (isSuccess) {
                                        logger.info("消息发送成功:" + msg);
                                    } else {
                                        logger.info("消息发送失败:");
                                    }

                                }
                            });
                        } catch (Exception e) {
                            logger.error(e.getMessage(), e);
                        }
                    }
                }
                faceDetectResDTOS.add(faceDetectResDTO);

            }
        }
        long end = System.currentTimeMillis();
        logger.debug("耗时:" + (end - start) + "ms");
        return Response.newSuccessResponse(faceDetectResDTOS);
    }


    @RequestMapping(value = "/detectFaces", method = RequestMethod.POST)
    @ResponseBody
    public Response<List<FaceDetectResDTO>> detectFaces(String image) {

        byte[] bytes = Base64Util.base64ToBytes(image);
        ImageInfo rgbData = ImageFactory.getRGBData(bytes);
        List<FaceDetectResDTO> faceDetectResDTOS = Lists.newLinkedList();
        List<FaceInfo> faceInfoList = faceEngineService.detectFaces(rgbData);
        if (CollectionUtil.isNotEmpty(faceInfoList)) {
            List<ProcessInfo> process = faceEngineService.process(rgbData, faceInfoList);

            for (int i = 0; i < faceInfoList.size(); i++) {
                FaceDetectResDTO faceDetectResDTO = new FaceDetectResDTO();
                FaceInfo faceInfo = faceInfoList.get(i);
                faceDetectResDTO.setRect(faceInfo.getRect());
                faceDetectResDTO.setOrient(faceInfo.getOrient());
                faceDetectResDTO.setFaceId(faceInfo.getFaceId());
                if (CollectionUtil.isNotEmpty(process)) {
                    ProcessInfo processInfo = process.get(i);
                    faceDetectResDTO.setAge(processInfo.getAge());
                    faceDetectResDTO.setGender(processInfo.getGender());
                    faceDetectResDTO.setLiveness(processInfo.getLiveness());
                }
                faceDetectResDTOS.add(faceDetectResDTO);

            }
        }

        return Response.newSuccessResponse(faceDetectResDTOS);
    }

    @RequestMapping(value = "/compareFaces", method = RequestMethod.POST)
    @ResponseBody
    public Response<Float> compareFaces(String image1, String image2) {

        byte[] bytes1 = Base64Util.base64ToBytes(image1);
        byte[] bytes2 = Base64Util.base64ToBytes(image2);
        ImageInfo rgbData1 = ImageFactory.getRGBData(bytes1);
        ImageInfo rgbData2 = ImageFactory.getRGBData(bytes2);

        Float similar = faceEngineService.compareFace(rgbData1, rgbData2);

        return Response.newSuccessResponse(similar);
    }

}
